package com.project.fortuneteller.websocketHandler;

import cn.hutool.json.JSONUtil;
import com.project.fortuneteller.pojo.Message;
import com.project.fortuneteller.pojo.vo.MessageWithFriendDto;
import com.project.fortuneteller.service.impl.MessageServiceImpl;
import com.project.fortuneteller.service.impl.UserServiceImpl;
import com.project.fortuneteller.utils.Result;
import com.project.fortuneteller.utils.ThreeCache;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * websocket 处理程序
 */
@Slf4j
@Component
public class WsHandler extends AbstractWebSocketHandler {

    /**
     * 记录 每个客户端的用户的建立连接的唯一表示
     */
//    private static String userToken;
    /**
     * 存储的是 连接的客户端的详细属性封装
     */
    private static final Map<String, SessionBean> sessionBeanMap;

    @Resource
    private RedissonClient redissonClient;

    /**
     * 存储 userid < -- > session_id
     */
    private static final Map<String, String > userIdToSessionMap;
    /**
     * 开启线程池
     */
    private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(50);

    @Resource
    private MessageServiceImpl messageService;

    @Resource
    private UserServiceImpl userService;


    static {
        // 从 token 到 userId
        sessionBeanMap = new ConcurrentHashMap<>();
        // 从 userId 到 token
        userIdToSessionMap = new ConcurrentHashMap<>();
        /**
         * 暂存消息
         *    如果 接受者还没有消息 但一个用户给该用户发送了 一个消息 那么 先将消息暂存当前 这个 Map中
         */
        Map<Integer, List<String>> messageTempStorageMap = new ConcurrentHashMap<>();
    }

    private final HttpMessageConverters messageConverters;

    public WsHandler(HttpMessageConverters messageConverters) {
        this.messageConverters = messageConverters;
    }

    /**
     *  建立连接后
     *        的处理
     *     关键是构造一种、指向性 即  webSessionId --- 》 token   --- 》 userId
     */

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        super.afterConnectionEstablished(session);
        String query = Objects.requireNonNull(session.getUri()).getQuery();
        // 用户 token
        String userToken = "";
        if( query!=null && !query.isEmpty()){
           // 分割
           String[] split = query.split("=");
           // 获取  token 的 值
           userToken = split[1];
        }
       // 从缓存中获取userId
        Integer userId = (Integer)ThreeCache.get(userToken);

        log.info(  "前端websocket请求参数: {}", query);

        //sessionBean的创建  与将 serId封装入 SessionBean
        SessionBean sessionBean = null;
        if(userId != null){
            sessionBean = new SessionBean(session,userId.toString());
        }

        // 这里需要取出前端传过来的  token 对象
       // 先判断 有则是上次会话遗留的  要删除
        if(userId != null) {
            if (userIdToSessionMap.containsKey(userId.toString())) {
                // 从 userIdToSessionMap中删除
                sessionBeanMap.remove(session.getId());
            }
            // 创建 userid 到  sessionId 的映射
            userIdToSessionMap.put(userId.toString(), session.getId());

        }
        sessionBeanMap.put(session.getId(), sessionBean);
        // 建立链接的 服务器
        log.info("建立连接的是: {}", sessionBeanMap.get(session.getId()).getWebSocketSession());

    }

    @Override
    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
        super.handleBinaryMessage(session, message);
    }

    /**
     *  收到消息
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        super.handleTextMessage(session, message);
        log.info("收到了消息  sessionBeanMa: {}:{}",
                sessionBeanMap.get(session.getId()).getClientUser(),
                message.getPayload());
        // 添加信息
        MessageWithFriendDto messageWithFriendDto = JSONUtil.toBean(message.getPayload(),
                MessageWithFriendDto.class);
       // 通过 token 获取 userId
        Integer userId = (Integer) ThreeCache.get(messageWithFriendDto.getSenderToken());
        // 获取信息
        Message messageObj = Message.builder().content(messageWithFriendDto.getContent())
                // 发送方
                .senderId(userId)
                .type(1)
                .createTime(new Date())
                .receiverId(messageWithFriendDto.getReceiverId())
                .build();

        // 返回 前端会用到
        messageWithFriendDto.setStatus(1);
        messageWithFriendDto.setSenderId(userId.toString());

        //  .status(messageWithFriendDto.getStatus())
        // 判断接收者是否在线
        String res = userIdToSessionMap.get(messageObj.getReceiverId().toString());
        if(res==null){
            // 不在线 设为 未读状态
            messageObj.setStatus(0);
        }else{
            //  否则设为 已读
            messageObj.setStatus(1);
        }
        // 消息的处理:  将消息添入 消息的历史记录当中
        // 消息的插入 @TODO 使用线程池来处理
        log.info( " 消息缓存到 redis 中  : {}" , messageObj );
        RMap<Object, Object> map = redissonClient.getMap("Message");
         // 保存一次就可以, 原因看获取消息的逻辑就知道了
        map.put("message:" +  System.currentTimeMillis()  + ":" + userId, messageObj);

        /**
         * 消息的转发
         */
        // 1. 先取出 消息的接收方的 Id
        int receiverId = messageWithFriendDto.getReceiverId();

        // 判断 接收者的Id 是否在userIdToSessionMap中 及是否 已经注册 webSocket服务

        /**
         *  不存在 还未注册 webSocket服务 则不发送 否者 发送消息
         *  这里可能要注意 整型转 String
         */

       if(userIdToSessionMap.containsKey(Integer.toString(receiverId))) {
            sessionBeanMap.get( userIdToSessionMap.get(Integer.toString(receiverId)) )
                    // 发送 文本 消息 及 线管
                    .getWebSocketSession().sendMessage(new TextMessage( JSONUtil.toJsonStr(messageWithFriendDto)  ));
        }
    }

    /**
     *  传输异常
     * @param session
     * @param exception
     * @throws Exception
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        super.handleTransportError(session, exception);
        // 判断是否是打开的状态
        if(session.isOpen()){
            session.close();
        }
        // 移除 ID
        sessionBeanMap.remove(session.getId());
    }

    /**
     *  连接关闭
     * @param session
     * @param status
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        super.afterConnectionClosed(session, status);
        SessionBean sessionBean = sessionBeanMap.get(session.getId());
        if(sessionBean != null){
            log.info("关闭连接的是: {}", sessionBean.getClientUser());
        }
        //
        // 关闭连接后 删除
        sessionBeanMap.remove(session.getId());
    }




}
